﻿using System.Xml.Serialization;

namespace Moly.Com
{
    using System;
    using System.Collections;
    using System.Collections.Generic;

    [Serializable]
    [XmlInclude(typeof(ModuleID))]
    public class ModuleList : IEnumerable<ModuleID>
    {
        [XmlArray]
        private readonly List<ModuleID> _list;
        public class ModuleMergeException : Exception
        {
            private ModuleID existing;
            private ModuleID adding;

            public ModuleMergeException(ModuleID existing, ModuleID adding)
                : base(string.Format("Incompatible dll already in list\n\tFirst :{0}\n\tSecond:{1}", existing, adding))
            {
                this.existing = existing;
                this.adding = adding;
            }

            public ModuleID Existing
            {
                get { return existing; }
            }

            public ModuleID Adding
            {
                get { return adding; }
            }
        }
        private ModuleList(List<ModuleID> rf)
        {
            _list = new List<ModuleID>();
            _list.AddRange(rf);
        }
        public ModuleList()
        {
            _list = new List<ModuleID>();
        }

        public int Count
        {
            get { return _list.Count; }
        }

        public ModuleID Add(ModuleID id)
        {
            if (HasExact(id))
                return id;

            if (HasCompatible(id))
            {
                var deja = _list.Find(id.InterfaceCompatible);
                var bestOf = ModuleID.GetBetterVersion(deja, id);
                deja.UpdateIf(bestOf);
                return deja;
            }
            ModuleID inc;
            if(HasInCompatible(id, out inc))
                throw new ModuleMergeException(inc, id);

            _list.Add(id);
            return id;
        }

        public bool HasInCompatible(ModuleID id, out ModuleID first)
        {
            var moduleList = FindAll(p => p.ModuleInCompatible(id));
            first = moduleList.Count > 0 ? moduleList[0] : null;
            return moduleList.Count != 0;
        }

        public bool HasExact(ModuleID id)
        {
            return _list.Contains(id);
        }
        public bool HasCompatible(ModuleID id)
        {
            return _list.Find(d => d.InterfaceCompatible(id)) != null;
        }

        public ModuleID this[int index]
        {
            get { return _list[index]; }
        }

        public ModuleList FindAll(Predicate<ModuleID> p)
        {
            return new ModuleList(_list.FindAll(p));
        }

        public void ForEach(Action<ModuleID> act)
        {
            _list.ForEach(act);
        }

        public IEnumerator<ModuleID> GetEnumerator()
        {
            return _list.GetEnumerator();
        }

        public ModuleID Find(Predicate<ModuleID> func)
        {
            return _list.Find(func);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public bool Has(ModuleID moduleId)
        {
            return
                _list.Find(p =>
                           p.Clsid == moduleId.Clsid &&
                           p.Lcid == moduleId.Lcid &&
                           p.Version.Compare(moduleId.Version) ==0) != null;

        }
    }
}